﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading;

namespace WatchDog
{
    public class Watcher
    {
        #region C'tor
        public Watcher(IEnumerable<string> ipsList)
        {
            // Init ip adresses
           
            SetIpAdresses(ipsList);
            // create the thread and run it...
            this.watchThread = new Thread(this.PerformCheck);
            this.watchThread.Start();
        }
        #endregion C'tor

        #region Members

        private readonly List<IPAddress> ipAdresses = new List<IPAddress>();
        private bool stopWatch;
        private Thread watchThread;

        public delegate void OnSuccessHandler(string ip, string hostName);
        public delegate void OnFailedHandler(string ip);

        public static event OnSuccessHandler Success;
        public static event OnFailedHandler Failed;
        #endregion Members

        #region Methods

        private static void OnFailed(string ip)
        {
            if (Failed != null)
            {
                Failed(ip);
            }
        }

        private static void OnSuccess(string ip, string hostName)
        {
            if (Success != null)
            {
                Success(ip, hostName);
            }
        }

        public void UpdateWatchList(IEnumerable<string> ipsList)
        {
            // Stop running thread
            this.stopWatch = true;
            Thread.Sleep(500);
            // Update ips
            SetIpAdresses(ipsList);
            this.stopWatch = false;
            // create the thread and run it...
            this.watchThread = new Thread(this.PerformCheck);
            this.watchThread.Start();
        }

        private void SetIpAdresses(IEnumerable<string> ipsList)
        {
            this.ipAdresses.Clear();
            foreach (var ipAdress in ipsList)
            {
                this.ipAdresses.Add(IPAddress.Parse(ipAdress));
            }
        }

        public void StopWatch()
        {
            this.stopWatch = true;
        }

        private void PerformCheck()
        {
            var ping = new Ping();
            while (!stopWatch)
            {
                try
                {
                    foreach (var ip in this.ipAdresses)
                    {
                        if (stopWatch) break;
                        PingReply replay = ping.Send(ip);
                        if (replay == null)
                        {
                            Console.WriteLine("Ping to {0} returned null.", ip);
                            UtilMessage.LogUtils.WriteToLog(String.Format("Ping to {0} returned null.", ip));
                            continue;
                        }
                        if (replay.Status == IPStatus.Success)
                        {
                            Console.WriteLine("Ping to {0} [{1}] successfully - {2} bytes in {3} ms.", ip,
                                              replay.Address,
                                              replay.Buffer.Length,
                                              replay.RoundtripTime);
                            UtilMessage.LogUtils.WriteToLog(String.Format("Ping to {0} [{1}] successfully - {2} bytes in {3} ms.", ip,
                                             replay.Address,
                                             replay.Buffer.Length,
                                             replay.RoundtripTime));
                            string hostName;
                            try
                            {
                                var hostEntry = Dns.GetHostEntry(ip);
                                hostName = hostEntry.HostName;
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                                hostName = "Cannot get host name";
                                UtilMessage.LogUtils.WriteToLog(String.Format("Cannot get host name from ip: {0}",ip));
                            }

                            OnSuccess(replay.Address.ToString(), hostName);
                        }
                        else
                        {
                            Console.WriteLine("Ping to {0} failed.", ip);
                            UtilMessage.LogUtils.WriteToLog(String.Format("Ping to {0} failed.", ip));
                            OnFailed(ip.ToString());
                        }
                    }
                }
                catch (Exception exception)
                {
                    Console.WriteLine(exception.Message);
                }
            }
        }
        #endregion Methods
    }
}
